package editors.objectedit;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Vector;

import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;

import physics.Body;
import physics.collision.RayCastInfo;
import physics.shapes.Polygon;

import tools.Help;
import tools.Point;

import editors.gui.GuiButton;
import editors.gui.GuiHandler;
import editors.gui.GuiLabel;
import editors.gui.GuiSettings;
import editors.gui.GuiText;

import game.Game;
import game.controlling.KeyListener;
import game.objects.GODynamic;
import game.objects.GOMap;
import game.waypoint.Waypoint;
import game.waypoint.WaypointSystem;
import graphics.main.MainRenderer;
import graphics.objects.GPolygon;
import graphics.system.Color;
import graphics.system.GLTools;
public class WaypointEditor extends MainRenderer {
	private static final float STEPSIZE = 80;
	private static final float WAYPOINTHEIGHT = 20;
	private static final float WAYPOINTRAD = 5;
	private static float GRIDSIZE = 100;
	private static final float CONNECTRADIUS = STEPSIZE * 1.1f;
	private static final float CONNECTRADIUSGRID = GRIDSIZE * 1.6f;
	private GODynamic gobj;
	private GuiHandler ghandler;
	private GuiHandler fname;
	WaypointSystem wsys;
	private enum State {
		ADD, TEST
	}
	State state;
	private boolean connect;
	private Waypoint wp;
	private Waypoint startp;
	private Waypoint endp;
	private ArrayList<Waypoint> path;
	private Point cmove;
	private boolean hasFileName;
	private String filename;
	public WaypointEditor() {
		super();
		this.simplerender = true;
		this.showConsole = false;
		this.showprofiler = false;
		initGui();
		state = State.ADD;
		connect = false;
		hasFileName = false;
	}
	public void setGame(Game g) {
		game = g;
		keyListener = new KeyListener();
		g.setKeyListener(keyListener);
		g.engine.graphics.setRenderer(this);
		this.gobj = new GODynamic(g);
		g.addGameObject(gobj);
		wsys = new WaypointSystem();
		g.engine.handle();
		g.engine.stopPhysics();
	}
	private void initGui() {
		this.ghandler = new GuiHandler();
		Point g = new Point(10, height - 10);
		ghandler.addGuiObject("label", new GuiLabel(g, 200, "WaypointEditor"));
		g.y -= GuiSettings.LINEHEIGHT + 2;
		ghandler.addGuiObject("add", new GuiButton(g, 98, "Add"));
		ghandler.addGuiObject("test", new GuiButton(g.add(100, 0), 100, "Test"));
		g.y -= GuiSettings.LINEHEIGHT + 2;
		ghandler.addGuiObject("autoadd", new GuiButton(g, 200, "AutoAdd"));
		g.y -= GuiSettings.LINEHEIGHT + 2;
		ghandler.addGuiObject("save", new GuiButton(g, 98, "save"));
		ghandler.addGuiObject("load", new GuiButton(g.add(100, 0), 100, "load"));
		fname = new GuiHandler();
		g = new Point(10, height - 10);
		fname.addGuiObject("label", new GuiLabel(g, 300, "Level name:"));
		g.y -= GuiSettings.LINEHEIGHT + 2;
		fname.addGuiObject("fname", new GuiText(g, 300));
		g.y -= GuiSettings.LINEHEIGHT + 2;
		fname.addGuiObject("ok", new GuiButton(g.add(100, 0), 100, "ok"));
	}
	@Override
	public void handle() {
		control();
		editorControl();
		handleGraphicLogic();
		game.engine.handle();
		frame++;
	}
	private void editorControl() {
		if (hasFileName) {
			ghandler.moveTo(new Point(10, height - 10));
		}
		else {
			fname.moveTo(new Point(width / 2 - 150, height / 2 + 50));
		}
		if (fname.isClicked("ok")) {
			String s = fname.getText("fname");
			if (s != "") {
				try {
					gobj.loadFile(s, new Point());
					hasFileName = true;
					filename = s;
				} catch (Exception e) {
					Help.p(e.getLocalizedMessage());
				}
			}
			fname.unClickAll();
		}
		if (hasFileName) {
			if (ghandler.isClicked("add")) {
				connect = false;
				state = State.ADD;
			}
			else if (ghandler.isClicked("test")) {
				connect = false;
				state = State.TEST;
				Help.p("test");
			}
			else if (ghandler.isClicked("autoadd")) {
				autoAddWaypoints();
			}
			else if (ghandler.isClicked("save")) {
				save();
			}
			else if (ghandler.isClicked("load")) {
				load();
			}
		}
		if (ghandler.mouseIn(this.getMousePosOnScreen()))
			return;
		if (hasFileName) {
			switch (state) {
				case ADD : {
					if (lmb()) {
						wsys.addWaypoint(keyListener.mp, false);
					}
					else {
						if (rmb() && connect == false) {
							wp = wsys.getWaypointAt(keyListener.mp, GRIDSIZE / 2);
							if (wp != null) {
								connect = true;
							}
						}
						else if (rmb() && connect) {
							Waypoint wp2 = wsys.getWaypointAt(keyListener.mp, GRIDSIZE / 2);
							if (wp2 != null) {
								wsys.connect(wp, wp2, wp.pos.distance(wp2.pos));
							}
							connect = false;
						}
					}
					break;
				}
				case TEST : {
					if (lmb()) {
						wp = wsys.getWaypointAt(keyListener.mp, GRIDSIZE / 2);
						if (wp != null) {
							startp = wp;
							recalcpath();
						}
					}
					if (rmb()) {
						wp = wsys.getWaypointAt(keyListener.mp, GRIDSIZE / 2);
						if (wp != null) {
							endp = wp;
							recalcpath();
						}
					}
					break;
				}
			}
		}
		if (game.engine.isDophysics()) {
			// autoAddWaypoints();
		}
	}
	private void load() {
		try {
			FileInputStream f = new FileInputStream("resources/models/"+filename+".wps");
			DataInputStream in = new DataInputStream(f);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String c = "";
			String line = "";
			while ((line = br.readLine()) != null) {
				c = c + line;
			}
			Help.p(c);
			wsys.clear();
			wsys.fromFileString(c);
		} catch (FileNotFoundException e) {
			Help.p("could not open file!");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	private void save() {
		String s = wsys.getFileString();
		try {
			FileOutputStream f = new FileOutputStream("resources/models/" + filename + ".wps");
			f.write(s.getBytes());
			f.write("\n".getBytes());
			f.close();
		} catch (FileNotFoundException e) {
			Help.p("could not open file!");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	private void autoAddWaypoints() {
		/*
		 * for(float x=-2000;x<2000;x+=GRIDSIZE){ for(float y=-2000;y<2000;y+=GRIDSIZE){ if(!bodyAt(x,y)){ wsys.addWaypoint(new Point(x,y)); } } }
		 */
		wsys.clear();
		// Help.p("auto adding!");
		long s = System.currentTimeMillis();
		/*
		 * float maxdot = 0.5f; for(int i=0;i<gobj.numBodies;i++){ Body b = gobj.getBody(i); if((b.isStatic() || b.sleeps) && b.shape instanceof Polygon){ Polygon p = (Polygon)b.shape; for(int j=0;j<p.points.length;j++){ Point c = p.points[j]; Point dir = p.points[j+1==p.points.length ? 0 : j+1].sub(p.points[j]); float length = dir.length(); dir = dir.normalize(); Point n = p.lines[j+1==p.points.length ? 0 : j+1].normalize().normal(); if(n.mul(b.pos) - n.mul(p.points[j]) > 0){ n = n.mul(-1); } float dot = n.mul(new Point(0,1)); if(dot > maxdot){ addPoints(c,length,dir); } } } }
		 * 
		 * 
		 * Vector<Waypoint> wps = wsys.getWaypoints(); for(int i=0;i<wps.size()-1;i++){ for(int j=i+1;j<wps.size();j++){ Waypoint x = wps.get(i); Waypoint y = wps.get(j); float dist = x.pos.distance(y.pos); if(dist < CONNECTRADIUS){ RayCastInfo ri = gobj.castRay(x.pos, y.pos.sub(x.pos), 0); if(ri == null || ri.pos.distance(x.pos)>dist){ wsys.connect(x, y, dist); } } } } int offs = wps.size();
		 */
		int offs = 0;
		Vector<Waypoint> wps = wsys.getWaypoints();
		BoundingBox box = gobj.getBoundinboxBodies();
		GRIDSIZE = Math.max(100, Math.min(2000, (box.maxX - box.minX) / 50));
		Help.p("Gridsize:" + GRIDSIZE);
		for (float i = box.minX - 0.1f; i < box.maxX; i += GRIDSIZE) {
			for (float j = box.minY - 0.1f; j < box.maxY; j += GRIDSIZE) {
				if (!bodyAt(i, j) && wsys.getWaypointAt(new Point(i, j), WAYPOINTRAD * 10) == null) {
					wsys.addWaypoint(new Point(i, j), false);
				}
			}
		}
		Help.p("Added:" + wps.size() + " waypoints!");
		wps = wsys.getWaypoints();
		for (int i = 0; i < wps.size() - 1; i++) {
			for (int j = Math.max(offs, i + 1); j < wps.size(); j++) {
				Waypoint x = wps.get(i);
				Waypoint y = wps.get(j);
				float dist = x.pos.distance(y.pos);
				if (dist < GRIDSIZE * 1.8f) {
					RayCastInfo ri = gobj.castRay(x.pos, y.pos.sub(x.pos), 0);
					if (ri == null || ri.pos.distance(x.pos) > dist) {
						wsys.connect(x, y, dist);
					}
				}
			}
		}
		Help.p("time:" + (System.currentTimeMillis() - s) + " ms");
	}
	private void addPoints(Point s, float length, Point dir) {
		Point cp = new Point(s);
		float l = 0;
		while (l < length) {
			if (!bodyAt(cp.x, cp.y + WAYPOINTHEIGHT)) {
				wsys.addWaypoint(cp.add(0, WAYPOINTHEIGHT), true);
			}
			// wsys.addWaypoint(cp.add(0,WAYPOINTHEIGHT));
			cp.sAdd(dir.mul(STEPSIZE));
			l += STEPSIZE;
		}
	}
	private boolean bodyAt(float x, float y) {
		Body b = gobj.getBodyAt(new Point(x, y));
		if (b != null && (b.isStatic() || b.sleeps))
			return true;
		/*
		 * if(gobj.getBodyAt(new Point(x-freerad,y)) != null) return true; if(gobj.getBodyAt(new Point(x+freerad,y)) != null) return true; if(gobj.getBodyAt(new Point(x,y-freerad)) != null) return true; if(gobj.getBodyAt(new Point(x,y+freerad)) != null) return true;
		 */
		return false;
	}
	private void recalcpath() {
		if (startp != null && endp != null) {
			long s = System.currentTimeMillis();
			this.path = wsys.getPath(startp, endp, false);
			Help.p("time pathfind:" + (System.currentTimeMillis() - s) + " ms");
		}
		else {
			path = null;
		}
	}
	private boolean lmb() {
		return keyListener.isButtonTouched(0);
	}
	private boolean rmb() {
		return keyListener.isButtonTouched(1);
	}
	public void render() {
		super.render();
		this.startCamTransformation();
		wsys.draw();
		if (connect) {
			GLTools.drawLine(wp.pos, keyListener.mp, Color.white);
		}
		if (state == State.TEST) {
			if (path != null && path.size() > 0) {
				for (int i = 0; i < path.size() - 1; i++) {
					GLTools.drawLine(path.get(i).pos, path.get(i + 1).pos, Color.blue);
				}
			}
			if (startp != null) {
				GLTools.drawCircle(startp.pos, 8, 0, Color.green);
			}
			if (endp != null) {
				GLTools.drawCircle(endp.pos, 8, 0, Color.red);
			}
		}
		this.stopCamTransformation();
		if (!hasFileName)
			fname.drawAll();
		else {
			ghandler.drawAll();
		}
	}
	private boolean inEditMode() {
		return ghandler.inTextEditMode() || fname.inTextEditMode();
	}
	public void control() {
		keyListener.clear();
		keyListener.mp = this.getMousePosTransformed();
		while (Keyboard.next()) {
			keyListener.addKey(Keyboard.getEventKey(), Keyboard.getEventKeyState());
			if (inEditMode() && Keyboard.getEventKeyState()) {
				fname.applyKeyPress(Keyboard.getEventCharacter());
			}
		}
		while (Mouse.next()) {
			keyListener.addButton(Mouse.getEventButton(), Mouse.getEventButtonState());
		}
		if (inEditMode() && keyListener.isKeyTouched(Keyboard.KEY_DELETE)) {
			fname.deleteChar();
		}
		float movespeed = 5 * 1.0f / zoom;
		if (!inEditMode() && Keyboard.isKeyDown(Keyboard.KEY_UP)) {
			this.campos.y += movespeed;
		}
		if (!inEditMode() && Keyboard.isKeyDown(Keyboard.KEY_DOWN)) {
			this.campos.y -= movespeed;
		}
		if (!inEditMode() && Keyboard.isKeyDown(Keyboard.KEY_RIGHT)) {
			this.campos.x += movespeed;
		}
		if (!inEditMode() && Keyboard.isKeyDown(Keyboard.KEY_LEFT)) {
			this.campos.x -= movespeed;
		}
		if (keyListener.isButtonTouched(2)) {
			cmove = keyListener.mp;
		}
		if (keyListener.isButtonPressed(2)) {
			Point delta = keyListener.mp.sub(cmove).mul(-1);
			campos.sAdd(delta);
			cmove = keyListener.mp.add(delta);
		}
		int wh = Mouse.getDWheel();
		if (wh < 0) {
			this.setZoom(zoom * 0.9f);
		}
		else if (wh > 0) {
			this.setZoom(zoom * 1.1f);
		}
		if (!inEditMode() && keyListener.isKeyReleased(Keyboard.KEY_SPACE)) {
			gobj.saveLoadPhysicState();
			game.engine.startStopPhysics();
		}
		if (this.wp != null && this.state == State.ADD && keyListener.isKeyTouched(Keyboard.KEY_DELETE)) {
			wsys.remWaypoint(wp);
			connect = false;
			wp = null;
		}
		if (keyListener.isButtonReleased(0)) {
			if (!hasFileName)
				fname.handleClick(getMousePosOnScreen());
			else
				ghandler.handleClick(this.getMousePosOnScreen());
		}
		else {
			if (!keyListener.isButtonPressed(0)) {
				if (!hasFileName)
					fname.unClickAll();
				else
					ghandler.unClickAll();
			}
		}
	}
}
